package logs

/*
*非阻塞型日志
 */

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"runtime"
	"sync"
	"sync/atomic"
	"time"
)

type LogLevel int

const (
	DEBUG LogLevel = iota
	INFO
	WARN
	ERROR
	FATAL
	DATA
)

var levelNames = []string{
	"DEBUG",
	"INFO",
	"WARN",
	"ERROR",
	"FATAL",
	"DATA",
}

type LogEntry struct {
	Time    time.Time
	Level   LogLevel
	File    string
	Line    int
	Message string
}

type AsyncLogger struct {
	minLevel      LogLevel
	entries       chan *LogEntry // 主日志通道
	urgentEntries chan *LogEntry // 紧急日志通道
	writer        io.Writer
	file          *os.File
	filePath      string // 基础文件路径，如 ./logs/log.log
	baseName      string // 基础文件名，如 log
	extName       string // 文件扩展名，如 .log
	maxSize       int64
	currentSize   int64
	rotateType    string
	dateFormat    string
	currentDate   string // 当前日志文件对应的日期
	mu            sync.Mutex
	wg            sync.WaitGroup
	stopChan      chan struct{}
	closed        int32 // 使用原子操作
	fastShutdown  int32 // 快速关闭标志
}

func NewFileLogger(minLevel LogLevel, filePath string, maxSize int64, rotateType string) (*AsyncLogger, error) {
	dir := filepath.Dir(filePath)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return nil, fmt.Errorf("failed to create log directory: %v", err)
	}

	// 解析基础文件名和扩展名
	baseName := filepath.Base(filePath)
	extName := filepath.Ext(baseName)
	baseNameWithoutExt := baseName[:len(baseName)-len(extName)]

	// 获取当前日期和时间戳
	currentDate := time.Now().Format("20060102")
	timestamp := time.Now().Format("150405")

	// 创建带日期和时间戳的文件名
	datedFileName := fmt.Sprintf("%s_%s_%s%s", baseNameWithoutExt, currentDate, timestamp, extName)
	datedFilePath := filepath.Join(dir, datedFileName)

	// 尝试打开带日期和时间戳的文件
	file, err := os.OpenFile(datedFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		return nil, fmt.Errorf("failed to open log file: %v", err)
	}

	stat, err := file.Stat()
	if err != nil {
		_ = file.Close()
		return nil, fmt.Errorf("failed to get file stats: %v", err)
	}

	logger := &AsyncLogger{
		minLevel:      minLevel,
		entries:       make(chan *LogEntry, 10000),
		urgentEntries: make(chan *LogEntry, 100),
		writer:        file,
		file:          file,
		filePath:      filePath,
		baseName:      baseNameWithoutExt,
		extName:       extName,
		maxSize:       maxSize,
		currentSize:   stat.Size(),
		rotateType:    rotateType,
		dateFormat:    "20060102", // 使用数字格式日期
		currentDate:   currentDate,
		stopChan:      make(chan struct{}),
	}

	logger.wg.Add(1)
	go logger.processLogs()

	return logger, nil
}

func (l *AsyncLogger) processLogs() {
	defer l.wg.Done()

	// 设置每日轮转检查的ticker
	var dateTicker *time.Ticker
	if l.rotateType == "date" {
		// 计算到明天零点的时间
		now := time.Now()
		nextMidnight := time.Date(now.Year(), now.Month(), now.Day()+1, 0, 0, 0, 0, now.Location())
		durationUntilMidnight := nextMidnight.Sub(now)

		// 创建每天触发一次的ticker
		dateTicker = time.NewTicker(24 * time.Hour)

		// 首次触发设置为明天零点
		time.AfterFunc(durationUntilMidnight, func() {
			// 立即检查一次日期轮转
			l.checkDateRotate()

			// 然后启动每日定时器
			for range dateTicker.C {
				l.checkDateRotate()
			}
		})
		defer dateTicker.Stop()
	}

	for {
		select {
		case <-l.stopChan:
			// 正常关闭处理剩余日志
			if atomic.LoadInt32(&l.fastShutdown) == 0 {
				l.drainChannels()
			}
			return

		case entry := <-l.urgentEntries:
			// 优先处理紧急通道日志
			l.writeLog(entry)

		default:
			select {
			case entry := <-l.urgentEntries:
				l.writeLog(entry)
			case entry := <-l.entries:
				l.writeLog(entry)
			case <-l.stopChan:
				if atomic.LoadInt32(&l.fastShutdown) == 0 {
					l.drainChannels()
				}
				return
			}
		}
	}
}

// checkDateRotate 检查日期轮转
func (l *AsyncLogger) checkDateRotate() {
	l.mu.Lock()
	defer l.mu.Unlock()

	if atomic.LoadInt32(&l.closed) == 1 {
		return
	}

	currentDate := time.Now().Format(l.dateFormat)
	if l.currentDate != currentDate {
		if err := l.rotateFile(); err != nil {
			l.errorlog("Failed to rotate log file by date: %v\n", err)
		} else {
			l.currentDate = currentDate
		}
	}
}

// 排空通道中的剩余日志
func (l *AsyncLogger) drainChannels() {
	// 先处理紧急通道
	for {
		select {
		case entry := <-l.urgentEntries:
			l.writeLog(entry)
		default:
			goto DrainMain
		}
	}

DrainMain:
	// 再处理主通道
	for {
		select {
		case entry := <-l.entries:
			l.writeLog(entry)
		default:
			if l.file != nil {
				_ = l.file.Sync()
			}
			return
		}
	}
}

func (l *AsyncLogger) writeLog(entry *LogEntry) {
	l.mu.Lock()
	defer l.mu.Unlock()

	if atomic.LoadInt32(&l.closed) == 1 {
		return
	}

	// 在写入前检查是否需要轮转
	if l.file != nil {
		needRotate := false

		if l.rotateType == "size" && l.maxSize > 0 {
			// 预估写入后的大小
			logStr := fmt.Sprintf("[%s] [%s] [%s:%d] %s\n",
				entry.Time.Format("2006-01-02 15:04:05.000"),
				levelNames[entry.Level],
				filepath.Base(entry.File),
				entry.Line,
				entry.Message)

			if l.currentSize+int64(len(logStr)) >= l.maxSize {
				needRotate = true
			}
		} else if l.rotateType == "date" {
			currentDate := time.Now().Format(l.dateFormat)
			if l.currentDate != currentDate {
				needRotate = true
			}
		}

		if needRotate {
			if err := l.rotateFile(); err != nil {
				l.errorlog("Failed to rotate log file: %v\n", err)
			}
		}
	}

	logStr := fmt.Sprintf("[%s] [%s] [%s:%d] %s\n",
		entry.Time.Format("2006-01-02 15:04:05.000"),
		levelNames[entry.Level],
		filepath.Base(entry.File),
		entry.Line,
		entry.Message)

	if _, err := l.writer.Write([]byte(logStr)); err != nil {
		l.errorlog("Failed to write log: %v\n", err)
	}

	l.currentSize += int64(len(logStr))
}

func (l *AsyncLogger) rotateFile() error {
	if l.file == nil {
		return nil
	}

	if err := l.file.Close(); err != nil {
		return fmt.Errorf("failed to close log file: %v", err)
	}

	dir := filepath.Dir(l.filePath)
	currentDate := time.Now().Format(l.dateFormat)

	// 根据轮转类型构建新文件名
	var newPath string
	if l.rotateType == "date" {
		// 日期轮转：使用日期后缀
		newFileName := fmt.Sprintf("%s_%s%s", l.baseName, currentDate, l.extName)
		newPath = filepath.Join(dir, newFileName)
	} else {
		// 大小轮转：使用时间戳后缀，确保文件名唯一
		timestamp := time.Now().Format("150405")
		newFileName := fmt.Sprintf("%s_%s_%s%s", l.baseName, currentDate, timestamp, l.extName)
		newPath = filepath.Join(dir, newFileName)

		// 对于大小轮转，还需要创建新的当前日志文件（使用基础名称）
		currentLogPath := filepath.Join(dir, fmt.Sprintf("%s%s", l.baseName, l.extName))
		if _, err := os.Stat(currentLogPath); err == nil {
			// 如果基础文件已存在，先重命名它
			oldTimestamp := time.Now().Add(-time.Second).Format("150405")
			backupPath := filepath.Join(dir, fmt.Sprintf("%s_%s_%s%s", l.baseName, currentDate, oldTimestamp, l.extName))
			if err := os.Rename(currentLogPath, backupPath); err != nil {
				return fmt.Errorf("failed to rename current log file: %v", err)
			}
		}
	}

	// 创建新日志文件
	file, err := os.OpenFile(newPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		// 尝试重新打开原文件
		if originalFile, err := os.OpenFile(l.filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644); err == nil {
			l.file = originalFile
			l.writer = originalFile
		}
		return fmt.Errorf("failed to create new log file: %v", err)
	}

	// 更新logger状态
	stat, err := file.Stat()
	if err != nil {
		file.Close()
		return fmt.Errorf("failed to get file stats: %v", err)
	}

	l.file = file
	l.writer = file
	l.currentSize = stat.Size()
	l.currentDate = currentDate

	return nil
}

func (l *AsyncLogger) log(level LogLevel, urgent bool, format string, args ...any) {
	if level < l.minLevel {
		return
	}

	_, file, line, ok := runtime.Caller(2)
	if !ok {
		file = "???"
		line = 0
	}

	entry := &LogEntry{
		Time:    time.Now(),
		Level:   level,
		File:    file,
		Line:    line,
		Message: fmt.Sprintf(format, args...),
	}

	// 根据紧急程度选择通道
	if urgent {
		select {
		case l.urgentEntries <- entry:
		default:
			l.errorlog("Urgent log queue full, dropping log: %s\n", entry.Message)
		}
	} else {
		select {
		case l.entries <- entry:
		default:
			l.errorlog("Log queue full, dropping log: %s\n", entry.Message)
		}
	}
}

// Close 正常关闭 - 会处理完所有日志
func (l *AsyncLogger) Close() {
	if atomic.CompareAndSwapInt32(&l.closed, 0, 1) {
		close(l.stopChan)
		l.wg.Wait()

		l.mu.Lock()
		defer l.mu.Unlock()
		if l.file != nil {
			_ = l.file.Sync()
			_ = l.file.Close()
		}
	}
}

// FastClose 快速关闭 - 不处理剩余日志
func (l *AsyncLogger) FastClose() {
	atomic.StoreInt32(&l.fastShutdown, 1)
	l.Close()
}

// Debug 日志级别方法
func (l *AsyncLogger) Debug(format string, args ...any) {
	l.log(DEBUG, false, format, args...)
}

func (l *AsyncLogger) Info(format string, args ...any) {
	l.log(INFO, false, format, args...)
}

func (l *AsyncLogger) Warn(format string, args ...any) {
	l.log(WARN, false, format, args...)
}

func (l *AsyncLogger) Error(format string, args ...any) {
	l.log(ERROR, true, format, args...) // 错误日志使用紧急通道
}

func (l *AsyncLogger) Data(format string, args ...any) {
	l.log(DATA, true, format, args...)
}

func (l *AsyncLogger) Fatal(format string, args ...any) {
	l.log(FATAL, true, format, args...)
	l.FastClose() // Fatal直接快速关闭
	os.Exit(1)
}

func (l *AsyncLogger) errorlog(formats string, args ...any) {
	_, err := fmt.Fprintf(os.Stderr, formats, args...)
	if err != nil {
		return
	}
}
